<!DOCTYPE html>
<html>
        <head>
                <title>控制器</title>
                <meta charset="UTF-8">
                <meta name="viewport" content="width=device-width, initial-scale=1.0">
                <script src="js/inc.js"></script>
        </head>
        <body>
                <fieldset>
                        <legend>控制器</legend>
                        <ol>
                                <li>
                                        <h2 class="title_h2">存放路径</h2>
                                        控制器文件是放在<code>application/classes/Controller</code>文件夹里面。
                                        <br>classes类目录下的文件类名和它的文件的位置是对应的。
                                        <br>1.比如有文件：classes/Controller/CommonUser.php
                                        <br>那么CommonUser.php文件里面的类名就应该是：Controller_CommonUser。
					<br>2.比如有文件：classes/Controller/Vip/User.php
                                        <br>那么User.php文件里面的类名就应该是：Controller_Vip_User,也就是下划线代表着文件夹的分隔符。
                                </li>
                                <li>
                                        <h2 class="title_h2">命名规则</h2>
                                        控制器文件名要对应类名，必须带前缀<code>Controller_</code>，大小写敏感,例如：Controller_Welcome。
                                </li>
                                <li>
                                        <h2 class="title_h2">访问控制器</h2>
                                        控制器访问支持PATH_INFO和GET模式。
                                        <br><h3 class="title_h3">1.PATH_INFO模式</h3>
					比如访问：http://127.0.0.1/index.php/Welcome/index.do?id=123
                                        <br>1 这里假定控制器文件配置方法url后缀是.do，方法前缀是do，方法名首字母大写不包含前缀。
                                        <br>2 这个地址访问的是文件：<code>application/classes/Controller/Welcome.php</code>里面的Welcome类的do_index方法。
                                        <br>3 同时传递了一个get变量id，可以通过Sr::get('id');获取123。
                                        <br><h3 class="title_h3">2.GET模式</h3>
					1.比如访问：http://127.0.0.1/index.php?c=Welcome&a=index&id=123 
                                        <br>1、 这个地址访问的是文件：<code>application/classes/Controller/Welcome.php</code>里面的Welcome类的do_index方法。
                                        <br>2、 get模式中c参数是代表控制器，a参数是代码方法。
                                        <br>3、 同时传递了一个get变量id，可以通过Sr::get('id');获取123。
					<br>2.比如访问：http://127.0.0.1/index.php?c=Welcome&a=index&id=123&m=Demo
                                        <br>1、 这个地址访问的是文件：<code>application/hvmc/demo/classes/Controller/Welcome.php</code>里面的Welcome类的do_index方法。
                                        <br>2、 get模式中c参数是代表控制器，a参数是代码方法,m是hvmc模块名称。
                                        <br>3、 同时传递了一个get变量id，可以通过Sr::get('id')获取123。
                                        <br><b class="text_strong">提示：</b>
                                        <br>了解详细的路由，点击进入<a href="router.html">路由手册</a>

                                </li>

                                <li>
                                        <h2 class="title_h2">代码规范</h2>
                                        控制器类必须继承<code>Soter_Controller</code>类，类名首字母大写。
                                        <br>示例如下：
                                        <pre class="brush:php">
                                                class Controller_Welcome extends Soter_Controller {

                                                        public function do_index() {
                                                                echo Sr::get('id');
                                                        }
                                                }
                                        </pre>
                                </li>
				<li>
                                        <h2 class="title_h2">获取访问控制器的信息</h2>
					访问控制器的信息都存放在一个Soter_Route类对象里面，
					<br>我们可以通过Sr::config()->getRoute()获取当前访问控制器信息的Soter_Route对象。
					<br>比如下面代码：
					<pre class="brush:php">
                                                $route=Sr::config()->getRoute();
                                        </pre>
					然后我们就可以通过$route对象的一些方法获取信息。
					<br>Soter_Route对象有用的方法如下：
					<br>1.$route->getController() 获取当前访问的控制器类名称，比如:Controller_Welcome
					<br>2.$route->getControllerShort() 获取当前访问的不含Controller_前缀的控制器类名称，比如:Welcome
					<br>3.$route->getMethod() 获取当前访问的方法名称，比如:do_index
					<br>4.$route->getMethodShort() 获取当前访问的不含前缀的方法名称，比如:index
					<br>5.$route->getArgs() 获取url中的所有“参数”，比如:array('10','moive')，没有就是空
					<br>6.$route->getHmvcModuleName() 获取当前访问的HMVC模块名称，比如:Demo，没有就是空
				</li>
                                <li>
                                        <h2 class="title_h2">控制器相关的配置</h2>
                                        在入口文件里面我们可以看到下面的配置：
                                        <br>1.默认控制器
                                        <br><code>->setDefaultController('Welcome')</code>
                                        <br>2.默认方法
                                        <br><code>->setDefaultController('index')</code>
                                        <br>3.方法前缀
                                        <br><code>->setMethodPrefix('do_')</code>
                                        <br>4.方法url后缀
                                        <br><code>->setMethodUriSubfix('.do')</code>
                                        <br>5.当用户在浏览器地址里面只输入了域名，或者hvmc模块。没有输入控制器名称和方法名称。 
                                        <br>那么在这种情况下我们可以设置一个默认的控制器和默认的方法。
					<br>详细的内容参考<a href="router.html">路由手册</a>的“<b>默认控制器和默认方法</b>”部分。
                                </li>
				<li>
                                        <h2 class="title_h2">自动调用视图</h2>
					1.默认情况下，我们在控制器方法里直接echo字符串输出数据到浏览器，我们还可以return一个字符串同样可以输出到浏览器。
					<br>2.如果我们在控制器方法里面return一个数据数组
					<br>比如下面代码：
					<pre class="brush:php">
						class Controller_Welcome extends Soter_Controller {

							public function do_index() {
								$data=array('test'=>'hello');
								return $data;
							}
						}
					</pre>
					可以看到上面的do_index方法里面直接返回了一个数组数据，
					<br>那么Soter就会自动调用视图文件application/views/<b>Welcome</b>/<b>index</b>.php
					<br>并通过视图的set方法把数据$data传递给index.php视图文件,视图index.php里面可以直接使用$test变量了。
					<br>这种模式，视图位置和名称与<b>不含Controller_前缀的控制器名称</b>和<b>不含前缀的方法名称</b>是对应的。
					<br>控制器方法里面直接return数组，调用的视图文件和方法名称对应关系如下：
					<br>application/views/<b>不含Controller_前缀的控制器名称</b>/<b>不含前缀的方法名称</b>.php
				</li>
				<li>
                                        <h2 class="title_h2">方法缓存</h2>
                                        Soter可以在控制器方法级别上缓存方法的输出，开启这个功能很简单，只需要在入口文件里面进行配置即可。
					<br>我们看到入口文件里面有下面的配置：
					<pre class="brush:php">
						//设置控制器方法缓存规则，可以是配置文件名称，也可以是配置规则数组
						->setMethodCacheConfig('method_cache')
                                        </pre>
					通过传入配置文件名称可以让我们在不同环境下面自动使用不同的缓存策略，
					<br>我们只需要在不同的环境配置目录下面放一个method_cache.php即可，里面写上对应环境的配置。
					<br>method_cache.php内容如下：
					<pre class="brush:php">
						/**
						* 需要缓存的控制器方法缓存配置
						* 键规则是：控制器名称::方法名称
						* 控制器名称不需要Controller_前缀,方法名称不需要前缀。
						*/
					       return array(
						   'Welcome::index' => array(
						       'cache' => true, //是否开启缓存
						       'time' => 3600, //缓存时间，单位秒
						       'key' => function() {
							       //根据具体的业务逻辑，返回缓存key，
							       //返回的key如果为空，则不使用缓存
							       return 'userId:' . Sr::get('userId');
						       }
						   )
					       );
                                        </pre>
					我们还可以直接传入配置数组：
					<pre class="brush:php">
						->setMethodCacheConfig(
							array(
							   'Welcome::index' => array(
							       'cache' => true, //是否开启缓存
							       'time' => 3600, //缓存时间，单位秒
							       'key' => function() {
								       //根据具体的业务逻辑，返回缓存key，
								       //返回的key如果为空，则不使用缓存
								       return 'userId:' . Sr::get('userId');
							       }
							   )
						       )
						)
                                        </pre>
					直接传入配置数组的方式相对不够灵活，无论哪种环境我们的程序使用的都是一样的方法缓存配置。
					<br>使用传入配置数组还是使用传入配置文件名称，我们可以按着自己的需求来选择。
                                </li>
				<li>
                                        <h2 class="title_h2">前置方法和后置方法</h2>
					1.如果一个控制器有一个名字为before的方法，在Soter里面称为“前置方法”，
					<br>前置方法类似于类的构造方法，在web运行模式下，会在每个被访问的控制器方法执行之前执行before方法。
					<br>2.如果一个控制器有一个名字为after的方法，在Soter里面称为“后置方法”，
					<br>后置方法类似于类的析构方法，在web运行模式下，会在每个被访问的控制器方法执行之后执行after方法。
					<br>下面我们结合实例来理解这两个方法，比如有一个Welcome控制器类代码如下：
					<pre class="brush:php">
						 class Controller_Welcome extends Soter_Controller {

							public function before($method,$args){
								if($method=='testBefore'){
									echo 'before'.$args[0];
								}
							}
							public function after($method,$args,$contents){
								if($method=='testAfter'){
									echo 'after'.$args[0].$contents;
								}else{
									echo $contents;
								}
							}
							public function do_testBefore($a){
								echo 'x'.$a;
							}
							public function do_testAfter(){
								echo  'test';
							}
						}
                                        </pre>
					<b>A.当我们访问：/Welcome/testBefore-1.do，会输出：before1x1</b>
					<br>为什么会是before1x1，我们分析一下可以看到before方法在执行testBefore之前被执行了，
					<br>before方法里面判断被访问的方法$method是否是“testBefore”，如果是就输出before字符串和url中传递给testBefore方法的第一个参数，也就是before1。
					<br>接着testBefore方法被执行，testBefore输出了x和第一个参数1也就是x1，加上前面before方法输出的before1，最终结果就是before1x1。
					<br><b>B.当我们访问：/Welcome/testAfter-1.do，会输出：after1test</b>
					<br>为什么会是after1test，我们分析一下可以看到在testAfter方法被执行之后执行了after方法，testAfter方法里面输出量test，
					<br>然后after方法里面判断被访问的方法是否是testAfter，如果是就输出：“after字符串”和“1”和“testAfter方法的输出也就是test”，
					<br>那么被访问的方法是否是testAfter，最后结果就是after1test。
					<br>如果被访问的方法不是testAfter，则原样输出被方法的方法的输出内容$contents。
					<br><b>前置方法和前置方法参数介绍：</b>
					<br><b>1.可以看到前置方法before接受两个参数$method和$args，它们含义如下：</b>
					<br>$method　是不包含前缀的被访问的控制器方法名
					<br>$args　　是url中传递给被访问的控制器方法的参数数组
					<br><b>2.可以看到后置方法after接受三个参数$method、$args和$contents，它们含义如下：</b>
					<br>$method　　是不包含前缀的被访问的控制器方法名
					<br>$args　　　是url中传递给被访问的控制器方法的参数数组
					<br>$contents　是被访问的控制器方法的输出的字符串内容
					<br><b class="text_strong">提示：
					<br>如果后置方法不输出任何东西，那么访问控制器的任何方法，即使方法里面有输出，最终页面也不会有输出，
					<br>最终的输出是后置方法after决定的，所以后置方法里面要根据情况处理$contents，然后输出。
					</b>
				</li>

                        </ol>
                </fieldset>
                <script src="js/inc.foot.js"></script>
        </body>
</html>
